#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

#include<iostream>
using namespace std;

#define MAXCOMAND 1024
#define ARGC 64
#define SEP " "

enum redir{
  REDIR_INPUT=0,
  REDIR_OUTPUT,
  REDIR_APPEND,
  REDIR_NONE 
};
char* checkDir(char*comand,redir*redir_type)
{
  assert(comand&&redir_type);

  int pos=0;
  while(comand[pos]!='\0'&&comand[pos]!='>'&&comand[pos]!='<')
  {
    ++pos;
  }
  
  if(comand[pos]=='>')
  {
    comand[pos]='\0';
    if(comand[pos+1]=='>')
    {
      *redir_type=REDIR_APPEND;
      return comand+pos+2;
    }
    else 
    {
      *redir_type=REDIR_OUTPUT;
      return comand+pos+1;
    }
  }
  else if(comand[pos]=='<')
  {
    comand[pos]='\0';
    *redir_type=REDIR_INPUT;
    return comand+pos+1;
  }
  return NULL;
}

int split(char *comand,char*argv[])
{
  assert(comand);
  assert(argv);
  
  int i=0;
  argv[0]=strtok(comand,SEP);
  if(argv[0]==NULL)
  {
    return -1;
  }
  i=1;
  for(;argv[i]=strtok(NULL,SEP);++i);
}

void DebugPrint(char*argv[])
{
  for(int i=0;argv[i];++i)
  {
    cout<<argv[i]<<endl;
  }
}

void showenv()
{
  extern char** environ;
  for(int i=0;environ[i];++i)
  {
    printf("[%d]%s\n",i,environ[i]);
  }
}

int main()
{
  //当我们用env查看的时候，想要查看的是父进程/bash自己环境变量列表;
  extern char** environ;
  char myenv[32][256];
  int env_index=0;
  int last_exit_code=0;
  while(1)
  {
    redir redir_type=REDIR_NONE; 

    printf("[myshell@test]# ");
    fflush(stdout);
   
    //输入一行指令
    char comand[MAXCOMAND]={0};
    char*s= fgets(comand,MAXCOMAND,stdin);//s用来判断是否正确输入
    assert(s);
    (void)s;//保证在release版本发布的时候，因为去掉了assert，所以没有用s，而带来的编译警告，什么都没有做
    comand[strlen(comand)-1]='\0';//去掉末尾的'\n'
   // printf("%s\n",comand);
    char* filename=checkDir(comand,&redir_type);
   
    //分割字符串
    char*argv[ARGC]={NULL};
    split(comand,argv);
    //DebugPrint(argv);
   
    //内建命令-----------------------
    if(strcmp(argv[0],"cd")==0)
    {
      //说到底cd命令，重要表现就如同bash自己调用相应的函数
      if(argv[1]!=NULL)chdir(argv[1]);
      continue;
    }
    else if(strcmp(argv[0],"export")==0)//其实我们学到的环境变量命令，大部分都是内建命令
    {
      if(argv[1]!=NULL)
      {
       // putenv(argv[1]);
        strcpy(myenv[env_index],argv[1]);
        putenv(myenv[env_index++]);
      }
      continue;
    }
    else if(strcmp(argv[0],"env")==0)
    {
      showenv();
      continue;
    }
    else if(strcmp(argv[0],"echo")==0)
    {
      

      //echo $PATH
      const char* target_env=NULL;
      if(argv[1][0]=='$')
      {
        if(argv[1][1]=='?')
        {
          printf("%d\n",last_exit_code);
        }
        target_env=getenv(argv[1]+1);
      }
      if(target_env!=NULL)
      {
        printf("%s=%s\n",argv[1]+1,target_env);
      }
      continue;
    }


    //补充细节----------------------
    if(strcmp(argv[0],"ls")==0)
    {
      //在添加"--color=auto"字符串
      int i=0;
      while(argv[i]!=NULL)
      {
        ++i;
      }
     // strcpy(argv[i],"--color=auto");
     argv[i]=(char*)"--color=auto";
      argv[++i]=NULL;
    }



    //创建子进程
    pid_t id= fork();
    assert(id>=0);
    (void)id;
    if(id==0)
    {
      if(redir_type!=REDIR_NONE)
      {
        if(redir_type==REDIR_OUTPUT )
        {
          int fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
          dup2(fd,1);
        }
        else if(redir_type==REDIR_INPUT)
        {
          int fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
          dup2(fd,0);
        }
        else if(redir_type==REDIR_APPEND)
        {
          int fd = open(filename,O_WRONLY|O_CREAT|O_APPEND,0666);
          dup2(fd,1);
        }
      }
      //子进程
      execvpe(argv[0],argv,environ);
      exit(1);
    }
    

    //父进程等待子进程退出
    int status=0;
    pid_t ret=waitpid(id,&status,0);
    if(ret>0)
    {
      last_exit_code=WEXITSTATUS(status);
    }
  }

  return 0;
}
